package auth

import (
	"bytes"
	"fmt"
	"gitee.com/cjf8134/bbs_serve/internal/pkg/transports/http/middlewares/jwtoken"
	"gitee.com/cjf8134/bbs_serve/internal/pkg/utils/constutil"
	"gitee.com/cjf8134/bbs_serve/internal/pkg/utils/responseutil"
	"gitee.com/cjf8134/bbs_serve/internal/pkg/utils/stringutil"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"io/ioutil"
	"log"
	"net/http"
	"sort"
	"strconv"
	"strings"
	"time"
)

// 允许跨域
func Next() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method
		c.Header("Access-Control-Allow-Origin", "*")
		c.Header("Access-Control-Allow-Headers", "Access-Control-Allow-Headers,Authorization,User-Agent, Keep-Alive, Content-Type, X-Requested-With,X-CSRF-Token,AccessToken,Token,Sign,Timestamp")
		c.Header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT, PATCH, OPTIONS")
		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
		c.Header("Access-Control-Allow-Credentials", "true")
		// 放行所有OPTIONS方法
		if method == "OPTIONS" {
			c.AbortWithStatus(http.StatusAccepted)
		}
		c.Next()
	}
}

// CheckSignMiddleware 返回gin 签名验证
func CheckSignMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		jsonStr := make(map[string]interface{})
		_ = c.Request.ParseForm()
		req := c.Request.Form
		sign := c.GetHeader(constutil.SignParam)
		signTime := c.GetHeader(constutil.TimeParam)
		//获取原始基础请求参数验证
		if sign == "" || signTime == "" {
			responseutil.AbortFailJson(c,"sign和timestamp不能为空")
			return
		}
		// 非GET请求
		if len(req) == 0 {
			jsonObj, err := ioutil.ReadAll(c.Request.Body)
			c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(jsonObj))
			if err != nil {
				responseutil.AbortFailJson(c,"sign签名读取请求参数错误")
				return
			}
			// 解决json数字 默认int64处理,精度问题
			jsonStr = stringutil.CreateJsonUseNum(string(jsonObj))
		} else {
			// 从url获取参数
			for k, _ := range req {
				jsonStr[k] = req.Get(k)
			}
		}
		jsonStr[strings.ToLower(constutil.TimeParam)] = signTime
		//以下是验证sign的流程 参数名ASCII码从小到大排序（字典序）,参数的值为空不参与签名； appkey和appsecret不参与排序
		params := make([]string, 0, len(jsonStr))
		for key, v := range jsonStr {
			//sign不参与签名
			if key == constutil.SignParam {
				continue
			}
			if _, ok := v.(string); ok {
				if strings.TrimSpace(v.(string)) == "" { // 踢出空值
					continue
				}
			}
			params = append(params, key)
		}
		//把原始参数map按key排序
		sort.Strings(params)
		//2,把原始参数map按key排序后生成form格式参数  k1=v1&k2=v2&...
		// 首尾拼接key, secret
		var str = "key=" + constutil.AppKey //参数初始值
		for _, k := range params {
			str = str + fmt.Sprintf("&%v=%v", k, jsonStr[k])
		}
		str = str + "&secret=" + constutil.AppSecret
		originalSign := stringutil.MD5(str) // 签名值
		// 验证过期时间
		timestamp, err := strconv.ParseInt(signTime, 10, 64)
		now := time.Now().Unix()
		// timestamp到秒
		if now-timestamp > constutil.ExpireTime || err != nil { // 默认60s 超时
			responseutil.AbortFailJson(c,"签名已过期!")
			return
		}
		//4,对比sign
		if strings.Compare(sign, originalSign) != 0 {
			responseutil.AbortFailJson(c,"签名错误!")
			return
		}
		c.Next()
	}
}


// 基于JWT的认证中间件(获取用户名)
func JWTAuth() func(c *gin.Context) {
	return func(c *gin.Context) {
		auth := c.Request.Header.Get("Authorization")
		if auth == "" {
			responseutil.AbortFailJson(c,"请求头中auth为空")
			return
		}
		parts := strings.SplitN(auth, " ", 2)
		token, err := jwtoken.ParseToken(parts[1])
		if err != nil {
			responseutil.AbortFailJson(c,"无效的Token")
			return
		}
		log.Println("token == ",token)
		sess := sessions.Default(c)
		sess.Set("username", token.Username)
		sess.Set("userID", token.UserID)
		sess.Set("avatar", token.Avatar)
		c.Next()
	}
}
